#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author Jason, Mobvoi
# version 001
# Date: 20231101

import androidhelper
import time

droid = androidhelper.Android()
# 保持屏幕常亮
droid.wakeLockAcquireBright()

import os.path
import time
import math
#import numpy as np
import argparse
from smbus2 import SMBus
from random import randint




time_line = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
 

log_path = os.path.dirname(os.getcwd()) + '/'
logfile = log_path + time_line + '_bme.log'


i2c = SMBus(6) # /dev/i2c-%d
address = 0x76
data_read_type = "gas"
loop_times = 1
op_mode = 0


# Period between two polls (value can be given by user)
BME68X_PERIOD_POLL = 10000
# BME68X unique chip identifier
BME68X_CHIP_ID = 0x61
# Period for a soft reset
BME68X_PERIOD_RESET = 10000
# BME68X lower I2C address
BME68X_I2C_ADDR_LOW = 0x76
# BME68X higher I2C address
BME68X_I2C_ADDR_HIGH = 0x77
# Soft reset command
BME68X_SOFT_RESET_CMD = 0xb6
# Return code definitions
# Success
BME68X_OK = 0
# Errors
# Null pointer passed
BME68X_E_NULL_PTR = -1
# Communication failure
BME68X_E_COM_FAIL = -2
# Sensor not found
BME68X_E_DEV_NOT_FOUND = -3
# Incorrect length parameter
BME68X_E_INVALID_LENGTH = -4
# Self test fail error
BME68X_E_SELF_TEST = -5
# Warnings
# Define a valid operation mode
BME68X_W_DEFINE_OP_MODE = 1
# No new data was found
BME68X_W_NO_NEW_DATA = 2
# Define the shared heating duration
BME68X_W_DEFINE_SHD_HEATR_DUR = 3
# Information - only available via bme68x_dev.info_msg
BME68X_I_PARAM_CORR = 1
# Register map addresses in I2C
# Register for 3rd group of coefficients
BME68X_REG_COEFF3 = 0x00
# 0th Field address
BME68X_REG_FIELD0 = 0x1d
# 0th Current DAC address
BME68X_REG_IDAC_HEAT0 = 0x50
# 0th Res heat address
BME68X_REG_RES_HEAT0 = 0x5a
# 0th Gas wait address
BME68X_REG_GAS_WAIT0 = 0x64
# Shared heating duration address
BME68X_REG_SHD_HEATR_DUR = 0x6E
# CTRL_GAS_0 address
BME68X_REG_CTRL_GAS_0 = 0x70
# CTRL_GAS_1 address
BME68X_REG_CTRL_GAS_1 = 0x71
# CTRL_HUM address
BME68X_REG_CTRL_HUM = 0x72
# CTRL_MEAS address
BME68X_REG_CTRL_MEAS = 0x74
# CONFIG address
BME68X_REG_CONFIG = 0x75
# MEM_PAGE address
BME68X_REG_MEM_PAGE = 0xf3
# Unique ID address
BME68X_REG_UNIQUE_ID = 0x83
# Register for 1st group of coefficients
BME68X_REG_COEFF1 = 0x8a
# Chip ID address
BME68X_REG_CHIP_ID = 0xd0
# Soft reset address
BME68X_REG_SOFT_RESET = 0xe0
# Register for 2nd group of coefficients
BME68X_REG_COEFF2 = 0xe1
# Variant ID Register
BME68X_REG_VARIANT_ID = 0xF0
# Enable/Disable macros
# Enable
BME68X_ENABLE = 0x01
# Disable
BME68X_DISABLE = 0x00
# Variant ID macros
# Low Gas variant
BME68X_VARIANT_GAS_LOW = 0x00
# High Gas variant
BME68X_VARIANT_GAS_HIGH = 0x01
# Oversampling setting macros
# Switch off measurement
BME68X_OS_NONE = 0
# Perform 1 measurement
BME68X_OS_1X = 1
# Perform 2 measurements
BME68X_OS_2X = 2
# Perform 4 measurements
BME68X_OS_4X = 3
# Perform 8 measurements
BME68X_OS_8X = 4
# Perform 16 measurements
BME68X_OS_16X = 5
# IIR Filter settings
# Switch off the filter
BME68X_FILTER_OFF = 0
# Filter coefficient of 2
BME68X_FILTER_SIZE_1 = 1
# Filter coefficient of 4
BME68X_FILTER_SIZE_3 = 2
# Filter coefficient of 8
BME68X_FILTER_SIZE_7 = 3
# Filter coefficient of 16
BME68X_FILTER_SIZE_15 = 4
# Filter coefficient of 32
BME68X_FILTER_SIZE_31 = 5
# Filter coefficient of 64
BME68X_FILTER_SIZE_63 = 6
# Filter coefficient of 128
BME68X_FILTER_SIZE_127 = 7
# ODR/Standby time macros
# Standby time of 0.59ms
BME68X_ODR_0_59_MS = 0
# Standby time of 62.5ms
BME68X_ODR_62_5_MS = 1
# Standby time of 125ms
BME68X_ODR_125_MS = 2
# Standby time of 250ms
BME68X_ODR_250_MS = 3
# Standby time of 500ms
BME68X_ODR_500_MS = 4
# Standby time of 1s
BME68X_ODR_1000_MS = 5
# Standby time of 10ms
BME68X_ODR_10_MS = 6
# Standby time of 20ms
BME68X_ODR_20_MS = 7
# No standby time
BME68X_ODR_NONE = 8
# Operating mode macros
# Sleep operation mode
BME68X_SLEEP_MODE = 0
# Forced operation mode
BME68X_FORCED_MODE = 1
# Parallel operation mode
BME68X_PARALLEL_MODE = 2
# Sequential operation mode
BME68X_SEQUENTIAL_MODE = 3
# SPI page macros
# SPI memory page 0
BME68X_MEM_PAGE0 = 0x10
# SPI memory page 1
BME68X_MEM_PAGE1 = 0x00
# Coefficient index macros
# Length for all coefficients
BME68X_LEN_COEFF_ALL = 42
# Length for 1st group of coefficients
BME68X_LEN_COEFF1 = 23
# Length for 2nd group of coefficients
BME68X_LEN_COEFF2 = 14
# Length for 3rd group of coefficients
BME68X_LEN_COEFF3 = 5
# Length of the field
BME68X_LEN_FIELD = 17
# Length between two fields
BME68X_LEN_FIELD_OFFSET = 17
# Length of the configuration register
BME68X_LEN_CONFIG = 5
# Length of the interleaved buffer
BME68X_LEN_INTERLEAVE_BUFF = 20
# Coefficient index macros
# Coefficient T2 LSB position
BME68X_IDX_T2_LSB = 0
# Coefficient T2 MSB position
BME68X_IDX_T2_MSB = 1
# Coefficient T3 position
BME68X_IDX_T3 = 2
# Coefficient P1 LSB position
BME68X_IDX_P1_LSB = 4
# Coefficient P1 MSB position
BME68X_IDX_P1_MSB = 5
# Coefficient P2 LSB position
BME68X_IDX_P2_LSB = 6
# Coefficient P2 MSB position
BME68X_IDX_P2_MSB = 7
# Coefficient P3 position
BME68X_IDX_P3 = 8
# Coefficient P4 LSB position
BME68X_IDX_P4_LSB = 10
# Coefficient P4 MSB position
BME68X_IDX_P4_MSB = 11
# Coefficient P5 LSB position
BME68X_IDX_P5_LSB = 12
# Coefficient P5 MSB position
BME68X_IDX_P5_MSB = 13
# Coefficient P7 position
BME68X_IDX_P7 = 14
# Coefficient P6 position
BME68X_IDX_P6 = 15
# Coefficient P8 LSB position
BME68X_IDX_P8_LSB = 18
# Coefficient P8 MSB position
BME68X_IDX_P8_MSB = 19
# Coefficient P9 LSB position
BME68X_IDX_P9_LSB = 20
# Coefficient P9 MSB position
BME68X_IDX_P9_MSB = 21
# Coefficient P10 position
BME68X_IDX_P10 = 22
# Coefficient H2 MSB position
BME68X_IDX_H2_MSB = 23
# Coefficient H2 LSB position
BME68X_IDX_H2_LSB = 24
# Coefficient H1 LSB position
BME68X_IDX_H1_LSB = 24
# Coefficient H1 MSB position
BME68X_IDX_H1_MSB = 25
# Coefficient H3 position
BME68X_IDX_H3 = 26
# Coefficient H4 position
BME68X_IDX_H4 = 27
# Coefficient H5 position
BME68X_IDX_H5 = 28
# Coefficient H6 position
BME68X_IDX_H6 = 29
# Coefficient H7 position
BME68X_IDX_H7 = 30
# Coefficient T1 LSB position
BME68X_IDX_T1_LSB = 31
# Coefficient T1 MSB position
BME68X_IDX_T1_MSB = 32
# Coefficient GH2 LSB position
BME68X_IDX_GH2_LSB = 33
# Coefficient GH2 MSB position
BME68X_IDX_GH2_MSB = 34
# Coefficient GH1 position
BME68X_IDX_GH1 = 35
# Coefficient GH3 position
BME68X_IDX_GH3 = 36
# Coefficient res heat value position
BME68X_IDX_RES_HEAT_VAL = 37
# Coefficient res heat range position
BME68X_IDX_RES_HEAT_RANGE = 39
# Coefficient range switching error position
BME68X_IDX_RANGE_SW_ERR = 41
# Gas measurement macros
# Disable gas measurement
BME68X_DISABLE_GAS_MEAS = 0x00
# Enable gas measurement low
BME68X_ENABLE_GAS_MEAS_L = 0x01
# Enable gas measurement high
BME68X_ENABLE_GAS_MEAS_H = 0x02
# Heater control macros
# Enable heater
BME68X_ENABLE_HEATER = 0x00
# Disable heater
BME68X_DISABLE_HEATER = 0x01
# 0 degree Celsius
BME68X_MIN_TEMPERATURE = 0
# 60 degree Celsius
BME68X_MAX_TEMPERATURE = 60
# 900 hecto Pascals
BME68X_MIN_PRESSURE = 90000
# 1100 hecto Pascals
BME68X_MAX_PRESSURE = 110000
# 20% relative humidity
BME68X_MIN_HUMIDITY = 20
# 80% relative humidity
BME68X_MAX_HUMIDITY = 80
BME68X_HEATR_DUR1 = 1000
BME68X_HEATR_DUR2 = 2000
BME68X_HEATR_DUR1_DELAY = 1000000
BME68X_HEATR_DUR2_DELAY = 2000000
BME68X_N_MEAS = 6
BME68X_LOW_TEMP = 150
BME68X_HIGH_TEMP = 350
# Mask macros
# Mask for number of conversions
BME68X_NBCONV_MSK = 0X0F
# Mask for IIR filter
BME68X_FILTER_MSK = 0x1C
# Mask for ODR[3]
BME68X_ODR3_MSK = 0x80
# Mask for ODR[2:0]
BME68X_ODR20_MSK = 0xE0
# Mask for temperature oversampling
BME68X_OST_MSK = 0xE0
# Mask for pressure oversampling
BME68X_OSP_MSK = 0x1C
# Mask for humidity oversampling
BME68X_OSH_MSK = 0x07
# Mask for heater control
BME68X_HCTRL_MSK = 0x08
# Mask for run gas
BME68X_RUN_GAS_MSK = 0x30
# Mask for operation mode
BME68X_MODE_MSK = 0x03
# Mask for res heat range
BME68X_RHRANGE_MSK = 0x30
# Mask for range switching error
BME68X_RSERROR_MSK = 0xF0
# Mask for new data
BME68X_NEW_DATA_MSK = 0x80
# Mask for gas index
BME68X_GAS_INDEX_MSK = 0x0f
# Mask for gas range
BME68X_GAS_RANGE_MSK = 0x0f
# Mask for gas measurement valid
BME68X_GASM_VALID_MSK = 0x20
# Mask for heater stability
BME68X_HEAT_STAB_MSK = 0x10
# Mask for SPI memory page
BME68X_MEM_PAGE_MSK = 0x10
# Mask for reading a register in SPI
BME68X_SPI_RD_MSK = 0x80
# Mask for writing a register in SPI
BME68X_SPI_WR_MSK = 0x7f
# Mask for the H1 calibration coefficient
BME68X_BIT_H1_DATA_MSK = 0x0f
# Position macros
# Filter bit position
BME68X_FILTER_POS = 2
# Temperature oversampling bit position
BME68X_OST_POS = 5
# Pressure oversampling bit position
BME68X_OSP_POS = 2
# ODR[3] bit position
BME68X_ODR3_POS = 7
# ODR[2:0] bit position
BME68X_ODR20_POS = 5
# Run gas bit position
BME68X_RUN_GAS_POS = 4
# Heater control bit position
BME68X_HCTRL_POS = 3
# Ambient temperature in Degree C
BME68X_AMB_TEMP  = 25
# Macro to combine two 8 bit data's to form a 16 bit data
def BME68X_CONCAT_BYTES(msb, lsb):
    return ((msb << 8) | lsb)


class BME_DATA:
    def __init__(self):
        self.oversampling_humid = ""
        self.oversampling_press = ""
        self.oversampling_temp = ""
        self.heater_temp = ""
        self.heater_dur = ""
        self.shared_heaer_dur = ""
        self.iir_filter = ""
        self.odr = ""
        self.gas_enable = ""
        self.heater_dur_prof = ""
        self.heater_temp_prof = ""
        self.profile_len = ""
        self.variant_id = ""
        self.status = ""

# initialization
def_conf = BME_DATA()
def_conf.oversampling_humid = BME68X_OS_16X; # 16X oversampling rate 
def_conf.oversampling_press = BME68X_OS_1X; # 1X oversampling rate
def_conf.oversampling_temp = BME68X_OS_2X;  # 2X oversampling rate
def_conf.heater_temp = 300; # degree Celsius
def_conf.heater_dur = 100;  # milliseconds
def_conf.iir_filter = BME68X_FILTER_OFF
def_conf.odr = BME68X_ODR_NONE
def_conf.gas_enable = BME68X_ENABLE
def_conf.heatr_temp_prof = [0] * 10
def_conf.profile_len = 10




class CAL_DATA:
    def __init__(self):
        self.par_t1 = 0
        self.par_t2 = 0
        self.par_t3 = 0
        self.par_p1 = 0
        self.par_p2 = 0
        self.par_p3 = 0
        self.par_p4 = 0
        self.par_p5 = 0
        self.par_p6 = 0
        self.par_p7 = 0
        self.par_p8 = 0
        self.par_p9 = 0
        self.par_p10 = 0
        self.par_h1 = 0
        self.par_h2 = 0
        self.par_h3 = 0
        self.par_h4 = 0
        self.par_h5 = 0
        self.par_h6 = 0
        self.par_h7 = 0
        self.par_gh1 = 0
        self.par_gh2 = 0
        self.par_gh3 = 0
        self.t_fine = 0
        self.res_heat_range = 0
        self.res_heat_val = 0
        self.range_sw_err = 0

calibration_data = CAL_DATA()


read_data_array = [0] * BME68X_LEN_FIELD


class read_data_type:
    def __init__(self):
        self.status = 0
        self.gas_index = 0
        self.meas_index = 0
        self.res_heat = 0
        self.idac = 0
        self.gas_wait = 0
        self.temperature = 0
        self.pressure = 0
        self.humidity = 0
        self.gas_resistance = 0
read_data_value = read_data_type()


parser = argparse.ArgumentParser(description='Input parameters')
parser.add_argument('--target', '-t', type=str, default='gas', required=False, help="What do you wnat to test?")
parser.add_argument('--loop', '-l', type=int, default='10', required=False, help="How many times?")

args = parser.parse_args()
#print(args.target)
#print(args.loop)

SAMPLE_COUNT = 10
#data_read_type = args.target
#loop_times = args.loop


def delay_microseconds(microseconds):
    seconds = (microseconds / 1000000)
    
    time.sleep(seconds)  


def boundary_check(value, max):
    if value > max:
        value = max
    return value


def bme68x_read_chipid():
    # write to perform soft reset
    i2c.write_byte_data(address, BME68X_REG_SOFT_RESET, 0xb6)

    delay_microseconds(BME68X_PERIOD_RESET)
    
    # read chipID
    ret = i2c.read_byte_data(address, BME68X_REG_CHIP_ID)

    return ret


def bme68x_read_variant_id():
    # read chipID
    ret = i2c.read_byte_data(address, BME68X_REG_VARIANT_ID)
    
    def_conf.variant_id = ret

    return ret


def bme68x_oversampling_to_reg(val):
    return int(math.log2(val)) + 1


# This internal API is used to calculate the heater resistance value using float
def calc_res_heat_float(temp):
    var1 = None
    var2 = None
    var3 = None
    var4 = None
    var5 = None
    res_heat = None

    if temp > 400:  # Cap temperature
        temp = 400

    var1 = ((calibration_data.par_gh1 / 16.0) + 49.0)
    var2 = ((calibration_data.par_gh2 / 32768.0) * 0.0005 + 0.00235)
    var3 = (calibration_data.par_gh3 / 1024.0)
    var4 = (var1 * (1.0 + (var2 * temp)))
    var5 = (var4 + (var3 * BME68X_AMB_TEMP))
    res_heat = int(3.4 * ((var5 * (4 / (4 + calibration_data.res_heat_range)) * (1 / (1 + (calibration_data.res_heat_val * 0.002))))) - 25)

    return res_heat


# This internal API is used to calculate the gas wait
def calc_gas_wait(dur):
    factor = 0
    durval = None

    if dur >= 0xfc0:
        durval = 0xff  # Max duration
    else:
        while dur > 0x3F:
            dur = dur // 4
            factor += 1

        durval = dur + (factor * 64)

    return durval


def calc_heatr_dur_shared(dur):
    factor = 0
    heatdurval = 0

    if dur >= 0x783:
        heatdurval = 0xff  # 最大持续时间
    else:
        # 步长为0.477毫秒
        dur = int((dur * 1000) / 477)
        while dur > 0x3F:
            dur = dur >> 2
            factor += 1

        heatdurval = dur + (factor * 64)

    return heatdurval


def bme68x_set_op_mode(op_mode):
    tmp_pow_mode = 0
    pow_mode = 0
    reg_addr = BME68X_REG_CTRL_MEAS

    tmp_pow_mode = i2c.read_byte_data(address, BME68X_REG_CTRL_MEAS)

    pow_mode = tmp_pow_mode & BME68X_MODE_MSK
    if pow_mode != BME68X_SLEEP_MODE:
        tmp_pow_mode &= ~BME68X_MODE_MSK
        i2c.write_byte_data(address, reg_addr, tmp_pow_mode)
        delay_microseconds(BME68X_PERIOD_POLL)
    if pow_mode == BME68X_SLEEP_MODE:
        if op_mode != BME68X_SLEEP_MODE:
            tmp_pow_mode &= ~BME68X_MODE_MSK
            tmp_pow_mode |= op_mode & BME68X_MODE_MSK
            i2c.write_byte_data(address, reg_addr, tmp_pow_mode)
    
    return tmp_pow_mode


def bme68x_get_op_mode(op_mode):
    mode = 0

    mode = i2c.read_byte_data(address, BME68X_REG_CTRL_MEAS)
    op_mode = mode & BME68X_MODE_MSK

    return op_mode


def bme68x_read_calib():
    coeff_array = [0] * BME68X_LEN_COEFF_ALL

    coeff_array = i2c.read_i2c_block_data(address, BME68X_REG_COEFF1, BME68X_LEN_COEFF1)
    
    coeff_array[BME68X_LEN_COEFF1:] = i2c.read_i2c_block_data(address, BME68X_REG_COEFF2, BME68X_LEN_COEFF2)

    coeff_array[BME68X_LEN_COEFF1 + BME68X_LEN_COEFF2:] = i2c.read_i2c_block_data(address, BME68X_REG_COEFF3, BME68X_LEN_COEFF3)

    
    

    # Temperature related coefficients
    calibration_data.par_t1 = (coeff_array[BME68X_IDX_T1_MSB] << 8) | (coeff_array[BME68X_IDX_T1_LSB])
    calibration_data.par_t2 = (coeff_array[BME68X_IDX_T2_MSB] << 8) | (coeff_array[BME68X_IDX_T2_LSB])
    calibration_data.par_t3 = coeff_array[BME68X_IDX_T3]

    # Pressure related coefficients
    calibration_data.par_p1 = (coeff_array[BME68X_IDX_P1_MSB] << 8) | (coeff_array[BME68X_IDX_P1_LSB])
    calibration_data.par_p2 = (coeff_array[BME68X_IDX_P2_MSB] << 8) | (coeff_array[BME68X_IDX_P2_LSB])
    calibration_data.par_p3 = coeff_array[BME68X_IDX_P3]
    calibration_data.par_p4 = (coeff_array[BME68X_IDX_P4_MSB] << 8) | (coeff_array[BME68X_IDX_P4_LSB])
    calibration_data.par_p5 = (coeff_array[BME68X_IDX_P5_MSB] << 8) | (coeff_array[BME68X_IDX_P5_LSB])
    calibration_data.par_p6 = coeff_array[BME68X_IDX_P6]
    calibration_data.par_p7 = coeff_array[BME68X_IDX_P7]
    calibration_data.par_p8 = (coeff_array[BME68X_IDX_P8_MSB] << 8) | (coeff_array[BME68X_IDX_P8_LSB])
    calibration_data.par_p9 = (coeff_array[BME68X_IDX_P9_MSB] << 8) | (coeff_array[BME68X_IDX_P9_LSB])
    calibration_data.par_p10 = coeff_array[BME68X_IDX_P10]

    # Humidity related coefficients
    calibration_data.par_h1 = (coeff_array[BME68X_IDX_H1_MSB] << 4) | (coeff_array[BME68X_IDX_H1_LSB] & BME68X_BIT_H1_DATA_MSK)
    calibration_data.par_h2 = (coeff_array[BME68X_IDX_H2_MSB] << 4) | (coeff_array[BME68X_IDX_H2_LSB] >> 4)
    calibration_data.par_h3 = coeff_array[BME68X_IDX_H3]
    calibration_data.par_h4 = coeff_array[BME68X_IDX_H4]
    calibration_data.par_h5 = coeff_array[BME68X_IDX_H5]
    calibration_data.par_h6 = coeff_array[BME68X_IDX_H6]
    calibration_data.par_h7 = coeff_array[BME68X_IDX_H7]

    # Gas heater related coefficients
    calibration_data.par_gh1 = coeff_array[BME68X_IDX_GH1]
    calibration_data.par_gh2 = (coeff_array[BME68X_IDX_GH2_MSB] << 8) | (coeff_array[BME68X_IDX_GH2_LSB])
    calibration_data.par_gh3 = coeff_array[BME68X_IDX_GH3]

    # Other coefficients
    calibration_data.res_heat_range = (coeff_array[BME68X_IDX_RES_HEAT_RANGE] & BME68X_RHRANGE_MSK) >> 4
    calibration_data.res_heat_val = coeff_array[BME68X_IDX_RES_HEAT_VAL]
    calibration_data.range_sw_err = (coeff_array[BME68X_IDX_RANGE_SW_ERR] & BME68X_RSERROR_MSK) >> 4
    '''
    
    
    
    
    
    
    
    
    '''


def bme68x_set_chip_config():
    odr20 = 0
    odr3 = 1
    current_op_mode = 0
    
    reg_array = [0x71, 0x72, 0x73, 0x74, 0x75]
    data_array = [0] * 5
    
    bme68x_get_op_mode(current_op_mode)

    bme68x_set_op_mode(BME68X_SLEEP_MODE)
    
    data_array = i2c.read_i2c_block_data(address, reg_array[0], 5)
    
    #
    #

    def_conf.iir_filter = boundary_check(def_conf.iir_filter, BME68X_FILTER_SIZE_127)
    
    def_conf.oversampling_temp = boundary_check(def_conf.oversampling_temp, BME68X_OS_16X)
    
    def_conf.oversampling_press = boundary_check(def_conf.oversampling_press, BME68X_OS_16X)
    
    def_conf.oversampling_humid = boundary_check(def_conf.oversampling_humid, BME68X_OS_16X)
    
    def_conf.odr = boundary_check(def_conf.odr, BME68X_ODR_NONE)
    
    data_array[4] = ((data_array[4] & ~(BME68X_FILTER_MSK)) | (BME68X_FILTER_MSK & (def_conf.iir_filter << 2)))
    data_array[3] = ((data_array[3] & ~(BME68X_OST_MSK)) | (BME68X_OST_MSK & (def_conf.oversampling_temp << 5)))
    data_array[3] = ((data_array[3] & ~(BME68X_OSP_MSK)) | (BME68X_OSP_MSK & (def_conf.oversampling_press << 2)))
    data_array[1] = ((data_array[1] & ~(BME68X_OSH_MSK)) | (BME68X_OSH_MSK & (def_conf.oversampling_humid)))
    if def_conf.odr != BME68X_ODR_NONE:
        odr20 = def_conf.odr
        odr3 = 0
    
    data_array[4] = ((data_array[4] & ~(BME68X_ODR20_MSK)) | (BME68X_ODR20_MSK & (odr20 << 5)))
    data_array[4] = ((data_array[4] & ~(BME68X_ODR3_MSK)) | (BME68X_ODR3_MSK & (odr3 << 7)))
    #
    #
    
    for i in range (len(reg_array)):
        i2c.write_byte_data(address, reg_array[i], data_array[i])
    
    if (current_op_mode != BME68X_SLEEP_MODE):
        bme68x_set_op_mode(current_op_mode)


def set_conf(op_mode, nb_conv):
    rh_reg_addr = 0
    rh_reg_data = 0
    gw_reg_addr = 0
    gw_reg_data = 0
    heater_dur_shared_addr = BME68X_REG_SHD_HEATR_DUR

    if op_mode == BME68X_FORCED_MODE:
        rh_reg_addr = BME68X_REG_RES_HEAT0
        rh_reg_data = calc_res_heat(def_conf.heater_temp)
        gw_reg_addr = BME68X_REG_GAS_WAIT0
        gw_reg_data = calc_gas_wait(def_conf.heater_dur)
        nb_conv = 0

    #
    #
    #
    i2c.write_byte_data(address, rh_reg_addr, rh_reg_data)
    i2c.write_byte_data(address, gw_reg_addr, gw_reg_data)


def bme68x_set_gas_config(op_mode):
    nb_conv = 0
    hctrl = 0
    run_gas = 0
    ctrl_gas_data = [0, 0]
    ctrl_gas_addr = [BME68X_REG_CTRL_GAS_0, BME68X_REG_CTRL_GAS_1]

    bme68x_set_op_mode(BME68X_SLEEP_MODE)
    set_conf(op_mode, nb_conv)

    ctrl_gas_data = i2c.read_i2c_block_data(address, BME68X_REG_CTRL_GAS_0, 2)

    if def_conf.gas_enable == BME68X_ENABLE:
        hctrl = BME68X_ENABLE_HEATER
        if def_conf.variant_id == BME68X_VARIANT_GAS_HIGH:
            run_gas = BME68X_ENABLE_GAS_MEAS_H
        else:
            run_gas = BME68X_ENABLE_GAS_MEAS_H
    else:
        hctrl = BME68X_DISABLE_HEATER
        run_gas = BME68X_DISABLE_GAS_MEAS

    ctrl_gas_data[0] = ((ctrl_gas_data[0] & ~(BME68X_HCTRL_MSK)) | (BME68X_HCTRL_MSK & (hctrl << 3)))
    ctrl_gas_data[1] = ((ctrl_gas_data[1] & ~(BME68X_NBCONV_MSK)) | (BME68X_NBCONV_MSK & (nb_conv)))
    ctrl_gas_data[1] = ((ctrl_gas_data[1] & ~(BME68X_RUN_GAS_MSK)) | (BME68X_RUN_GAS_MSK & (run_gas << BME68X_RUN_GAS_POS)))
    
    #
    #
    #
    i2c.write_byte_data(address, ctrl_gas_addr[0], ctrl_gas_data[0])
    i2c.write_byte_data(address, ctrl_gas_addr[1], ctrl_gas_data[1])
    
    read_back_reg = i2c.read_i2c_block_data(address, 0x70, 6)
    #


def bme68x_set_mode(mode):
    ret = 0
    mode_set = 0
    if mode:
        mode_set = i2c.read_byte_data(address, BME68X_REG_CTRL_MEAS)
        mode_set = (mode_set & ~(BME68X_MODE_MSK)) | (BME68X_FORCED_MODE & BME68X_MODE_MSK)
        i2c.write_byte_data(address, BME68X_REG_CTRL_MEAS, mode_set)
        op_mode = 1
        

    else:
        mode_set = i2c.read_byte_data(address, BME68X_REG_CTRL_MEAS)
        mode_set = (mode_set & ~(BME68X_MODE_MSK)) | (BME68X_SLEEP_MODE & BME68X_MODE_MSK)
        i2c.write_byte_data(address, BME68X_REG_CTRL_MEAS, mode_set)


#This API is used to get the remaining duration that can be used for heating.
def bme68x_get_meas_dur(op_mode):
    meas_dur = 0
    meas_cycles = 0
    os_to_meas_cycles = [0, 1, 2, 4, 8, 16]

    boundary_check(def_conf.oversampling_temp, BME68X_OS_16X)
    boundary_check(def_conf.oversampling_press, BME68X_OS_16X)
    boundary_check(def_conf.oversampling_humid, BME68X_OS_16X)

    meas_cycles = os_to_meas_cycles[def_conf.oversampling_temp]
    meas_cycles += os_to_meas_cycles[def_conf.oversampling_press]
    meas_cycles += os_to_meas_cycles[def_conf.oversampling_humid]
    meas_dur = meas_cycles * 1963
    meas_dur += 477 * 4
    meas_dur += 477 * 5
    if op_mode != BME68X_PARALLEL_MODE:
        meas_dur += 1000

    return meas_dur


def bme68x_compensate_gas(gas_res_adc, gas_range):
    var1 = 0
    var2 = 0
    var3 = 0
    calc_gas_res = 0

    # Look up table for the possible gas range values
    lookupTable = [2147483647, 2147483647, 2147483647, 2147483647, 2147483647,
                2126008810, 2147483647, 2130303777,
                2147483647, 2147483647, 2143188679,
                2136746228, 2147483647, 2126008810,
                2147483647, 2147483647]

    var1 = ((1340 + (5 * calibration_data.range_sw_err)) *
            (lookupTable[gas_range])) >> 16
    var2 = ((gas_res_adc << 15) - 16777216) + var1
    var3 = ((125000 << (15 - gas_range)) * var1) >> 9
    var3 += (var2 >> 1)
    calc_gas_res = var3 // var2

    return calc_gas_res


def calc_temperature_float(temp_adc):
    var1 = ((((float(temp_adc) / 16384.0) - (float(calibration_data.par_t1) / 1024.0))) * (float(calibration_data.par_t2)))
    var2 = (((((float(temp_adc) / 131072.0) - (float(calibration_data.par_t1) / 8192.0))) * (((float(temp_adc) / 131072.0) - (float(calibration_data.par_t1) / 8192.0)))) * (float(calibration_data.par_t3) * 16.0))
    calibration_data.t_fine = (var1 + var2)
    calc_temp = ((calibration_data.t_fine) / 5120.0)
    return calc_temp


def calc_pressure_float(pres_adc):
    var1 = (((float(calibration_data.t_fine) / 2.0) - 64000.0))
    var2 = var1 * var1 * (((float(calibration_data.par_p6)) / (131072.0)))
    var2 = var2 + (var1 * ((float(calibration_data.par_p5)) * 2.0))
    var2 = (var2 / 4.0) + (((float(calibration_data.par_p4)) * 65536.0))
    var1 = (((((float(calibration_data.par_p3) * var1 * var1) / 16384.0) + ((float(calibration_data.par_p2) * var1))) / 524288.0))
    var1 = ((1.0 + (var1 / 32768.0)) * ((float(calibration_data.par_p1))))
    calc_pres = (1048576.0 - ((float(pres_adc))))
    if (int(var1) != 0):
        calc_pres = (((calc_pres - (var2 / 4096.0)) * 6250.0) / var1)
        var1 = (((float(calibration_data.par_p9)) * calc_pres * calc_pres) / 2147483648.0)
        var2 = calc_pres * (((float(calibration_data.par_p8)) / 32768.0))
        var3 = ((calc_pres / 256.0) * (calc_pres / 256.0) * (calc_pres / 256.0) * (calibration_data.par_p10 / 131072.0))
        calc_pres = (calc_pres + (var1 + var2 + var3 + ((float(calibration_data.par_p7)) * 128.0)) / 16.0)
    else:
        calc_pres = 0
    return calc_pres


def calc_humidity_float(hum_adc):
    temp_comp = ((calibration_data.t_fine) / 5120.0)
    var1 = (float(hum_adc)) - (((float(calibration_data.par_h1) * 16.0) + (((float(calibration_data.par_h3) / 2.0) * temp_comp))))
    var2 = var1 * ((float(((float(calibration_data.par_h2) / 262144.0) * (1.0 + (((float(calibration_data.par_h4) / 16384.0) * temp_comp) + (((float(calibration_data.par_h5) / 1048576.0) * temp_comp * temp_comp))))))))
    var3 = (float(calibration_data.par_h6) / 16384.0)
    var4 = (float(calibration_data.par_h7) / 2097152.0)
    calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2)
    if (calc_hum > 100.0):
        calc_hum = 100.0
    elif (calc_hum < 0.0):
        calc_hum = 0.0
    return calc_hum


# This internal API is used to calculate the gas resistance low value in float
def calc_gas_resistance_low_float(gas_res_adc, gas_range):
    calc_gas_res = None
    var1 = None
    var2 = None
    var3 = None
    gas_res_f = float(gas_res_adc)
    gas_range_f = 1 << gas_range
    lookup_k1_range = [
        0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8, 0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0
    ]
    lookup_k2_range = [
        0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8, -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    ]

    var1 = (1340.0 + (5.0 * calibration_data.range_sw_err))
    var2 = var1 * (1.0 + lookup_k1_range[gas_range] / 100.0)
    var3 = 1.0 + (lookup_k2_range[gas_range] / 100.0)
    calc_gas_res = 1.0 / (var3 * (0.000000125) * gas_range_f * (((gas_res_f - 512.0) / var2) + 1.0))

    return calc_gas_res


# This internal API is used to calculate the gas resistance value in float
def calc_gas_resistance_high_float(gas_res_adc, gas_range):
    calc_gas_res = None
    var1 = 262144 >> gas_range
    var2 = int(gas_res_adc) - 512

    var2 *= 3
    var2 = 4096 + var2

    calc_gas_res = 1000000.0 * var1 / var2

    return calc_gas_res


def calc_temperature(temp_adc):
    var1 = ((temp_adc >> 3) - (calibration_data.par_t1 << 1))
    var2 = (var1 * calibration_data.par_t2) >> 11
    var3 = ((var1 >> 1) * (var1 >> 1)) >> 12
    var3 = ((var3) * (calibration_data.par_t3 << 4)) >> 14
    calibration_data.t_fine = (var2 + var3)
    calc_temp = ((calibration_data.t_fine * 5) + 128) >> 8

    return calc_temp


def calc_pressure(pres_adc):
    #
    var1 = ((calibration_data.t_fine >> 1) - 64000)
    #
    var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calibration_data.par_p6) >> 2
    var2 = var2 + ((var1 * calibration_data.par_p5) << 1)
    var2 = (var2 >> 2) + (calibration_data.par_p4 << 16)
    var1 = ((((var1 >> 2) * (var1 >> 2)) >> 13) * (calibration_data.par_p3 << 5) >> 3) + ((calibration_data.par_p2 * var1) >> 1)
    #
    var1 = var1 >> 18
    #
    var1 = ((32768 + var1) * calibration_data.par_p1) >> 15
    #
    pressure_comp = 1048576 - pres_adc
    pressure_comp = (pressure_comp - (var2 >> 12)) * 3125
    pres_ovf_check = (1 << 31) >> 1
    if pressure_comp >= pres_ovf_check:
        pressure_comp = int(pressure_comp / var1) << 1
    else:
        pressure_comp = int((pressure_comp << 1) / var1)

    var1 = int(calibration_data.par_p9 * (((pressure_comp >> 3) * (pressure_comp >> 3)) >> 13)) >> 12
    var2 = ((pressure_comp >> 2) * calibration_data.par_p8) >> 13
    var3 = ((pressure_comp >> 8) * (pressure_comp >> 8) * (pressure_comp >> 8) * calibration_data.par_p10) >> 17
    pressure_comp = (pressure_comp) + ((var1 + var2 + var3 + (calibration_data.par_p7 << 7)) >> 4)

    return pressure_comp


def calc_humidity(hum_adc):
    temp_scaled = ((calibration_data.t_fine * 5) + 128) >> 8
    var1 = (hum_adc - (calibration_data.par_h1 * 16)) - (int((temp_scaled * calibration_data.par_h3) / 100) >> 1)
    var2 = int(calibration_data.par_h2 * (((temp_scaled * calibration_data.par_h4) / 100) + ((int(temp_scaled * ((temp_scaled * calibration_data.par_h5) / 100)) >> 6) / 100) + (1 << 14))) >> 10
    var3 = var1 * var2
    var4_1 = (calibration_data.par_h6 << 7) 
    var4_2 = int((temp_scaled * calibration_data.par_h7) / 100)
    var4_3 = var4_1 + (var4_2 >> 4)
    var4_4 = (var4_1 + var4_2) >> 4
    var4 = (calibration_data.par_h6 << 7) + int((temp_scaled * calibration_data.par_h7) / 100) >> 4
    var5 = int((var3 >> 14) * (var3 >> 14)) >> 10
    var6 = int(var4 * var5) >> 1
    calc_hum = ((int(var3 + var6) >> 10) * 1000) >> 12
    calc_hum /= 1000
    
    
    if calc_hum > 100:
        calc_hum = 100
    elif calc_hum < 0:
        calc_hum = 0

    return calc_hum


def calc_gas_resistance_low(gas_res_adc, gas_range):
    lookup_table1 = [2147483647, 2147483647, 2147483647, 2147483647, 2147483647,
                2126008810, 2147483647, 2130303777,
                2147483647, 2147483647, 2143188679,
                2136746228, 2147483647, 2126008810,
                2147483647, 2147483647]
    lookup_table2 = [4096000000, 2048000000, 1024000000, 512000000, 
                255744255, 127110228, 64000000, 
                32258064, 16016016, 8000000, 
                4000000, 2000000, 1000000, 
                500000, 250000, 125000]
    var1 = (1340 + (5 * calibration_data.range_sw_err)) * lookup_table1[gas_range] >> 16
    var2 = ((gas_res_adc << 15) - 16777216) + var1
    var3 = (lookup_table2[gas_range] * var1) >> 9
    calc_gas_res = (var3 + (var2 >> 1)) // var2
    
    
    return calc_gas_res


def calc_gas_resistance_high(gas_res_adc, gas_range):
    var1 = 262144 >> gas_range
    var2 = gas_res_adc - 512
    var2 *= 3
    var2 = 4096 + var2
    calc_gas_res = (10000 * var1) / var2
    calc_gas_res *= 100
    
    return calc_gas_res


def calc_res_heat(temp):
    if temp > 400:
        temp = 400
    var1 = (BME68X_AMB_TEMP * calibration_data.par_gh3) * 256 / 1000
    var2 = (calibration_data.par_gh1 + 784) * (((calibration_data.par_gh2 + 154009) * temp * 5) / 100 + 3276800) / 10
    var3 = var1 + (var2 / 2)
    var4 = var3 / (calibration_data.res_heat_range + 4)
    var5 = 131 * calibration_data.res_heat_val + 65536
    heatr_res_x100 = ((var4 / var5) - 250) * 34
    heatr_res = (heatr_res_x100 + 50) // 100
    return int(heatr_res)


def read_field_data(index):
    read_data_array = [0] * BME68X_LEN_FIELD
    gas_range_l = 0
    gas_range_h = 0
    adc_temp = 0
    adc_pres = 0
    adc_hum = 0
    adc_gas_res_low = 0
    adc_gas_res_high = 0

    read_data_array = i2c.read_i2c_block_data(address, BME68X_REG_FIELD0 + (index * BME68X_LEN_FIELD_OFFSET), BME68X_LEN_FIELD)

    read_data_value.status = read_data_array[0] & BME68X_NEW_DATA_MSK
    read_data_value.gas_index = read_data_array[0] & BME68X_GAS_INDEX_MSK
    read_data_value.meas_index = read_data_array[1]

    adc_pres = ((read_data_array[2] * 4096) | (read_data_array[3] * 16) | (read_data_array[4] // 16))
    adc_temp = ((read_data_array[5] * 4096) | (read_data_array[6] * 16) | (read_data_array[7] // 16))
    adc_hum = ((read_data_array[8] * 256) | read_data_array[9])
    adc_gas_res_low = (read_data_array[15] * 4 | (read_data_array[16] // 64))
    adc_gas_res_high = (read_data_array[15] * 4 | (read_data_array[16] // 64))
    gas_range_l = read_data_array[14] & BME68X_GAS_RANGE_MSK
    gas_range_h = read_data_array[16] & BME68X_GAS_RANGE_MSK

    
    
    
    if def_conf.variant_id == BME68X_VARIANT_GAS_HIGH:
        read_data_value.status |= read_data_array[16] & BME68X_GASM_VALID_MSK
        read_data_value.status |= read_data_array[16] & BME68X_HEAT_STAB_MSK
    else:
        read_data_value.status |= read_data_array[16] & BME68X_GASM_VALID_MSK
        read_data_value.status |= read_data_array[16] & BME68X_HEAT_STAB_MSK

    if (read_data_value.status & BME68X_NEW_DATA_MSK):
        read_data_value.res_heat = i2c.read_byte_data(address, BME68X_REG_RES_HEAT0 + read_data_value.gas_index)
        
        read_data_value.idac = i2c.read_byte_data(address, BME68X_REG_IDAC_HEAT0 + read_data_value.gas_index)
        
        read_data_value.gas_wait = i2c.read_byte_data(BME68X_REG_GAS_WAIT0 + read_data_value.gas_index, read_data_value.gas_wait)
        
        read_data_value.temperature = calc_temperature(adc_temp)
        read_data_value.pressure = calc_pressure(adc_pres)
        read_data_value.humidity = calc_humidity(adc_hum)
        
        if def_conf.variant_id == BME68X_VARIANT_GAS_HIGH:
            read_data_value.gas_resistance = calc_gas_resistance_high(adc_gas_res_high, gas_range_h)
        else:
            read_data_value.gas_resistance = calc_gas_resistance_high(adc_gas_res_low, gas_range_l)
    
    delay_microseconds(50 * BME68X_PERIOD_POLL)
            

def bme68x_get_data(op_mode):
    new_fields = 0
    field_ptr = BME_DATA
    field_ptr = [0]*3
    field_data = [0]*3

    field_ptr[0] = field_data[0]
    field_ptr[1] = field_data[1]
    field_ptr[2] = field_data[2]

    if op_mode == BME68X_FORCED_MODE:
        read_field_data(0)
        if read_data_value.status & BME68X_NEW_DATA_MSK:
            new_fields = 1
        else:
            new_fields = 0
        
    return new_fields 


def bme68x_init():
    bme68x_read_chipid()
    bme68x_read_variant_id()
    bme68x_read_calib()


# for debug, dump registers content
def register_dump():
    i = 0x0
    reg_val = 0
    while i <= 0x74:
        reg_val = i2c.read_byte_data(address, i)
        
        i = i + 1


#main functions
def forced_mode_main(sample_cnt):
    rslt = 0

    bme68x_init()

    bme68x_set_chip_config()

    bme68x_set_gas_config(BME68X_FORCED_MODE)

    local_sample_count = 1
    
    if sample_cnt <= 0:
        sample_cnt = SAMPLE_COUNT

    n_fields = 0

    while local_sample_count <= sample_cnt:
        bme68x_set_op_mode(BME68X_FORCED_MODE)

        delay_period = bme68x_get_meas_dur(BME68X_FORCED_MODE) + (def_conf.heater_dur * 1000)
        delay_microseconds(delay_period)

        n_fields = bme68x_get_data(BME68X_FORCED_MODE)

        local_sample_count += 1

#gTixi接口
def gasApp():

    bme68x_init()

    bme68x_set_chip_config()

    bme68x_set_gas_config(BME68X_FORCED_MODE)

    bme68x_set_op_mode(BME68X_FORCED_MODE)

    delay_period = bme68x_get_meas_dur(BME68X_FORCED_MODE) + (def_conf.heater_dur * 1000)
    delay_microseconds(delay_period)

    bme68x_get_data(BME68X_FORCED_MODE)
    
    result = {
        'temperature':read_data_value.temperature / 100,
        'pressure':read_data_value.pressure/1000, 
        'humidity':read_data_value.humidity, 
        'gas_resistance':read_data_value.gas_resistance/1000
        }
    sf='气体传感器\nT:{temperature:.1f}℃ P:{pressure:.1f}kPa\nH:{humidity:.1f}% R:{gas_resistance:.1f}kΩ\n↑暂停 ←退出'
    
    return sf.format(**result)
        
        

